/*
 *  Workspace window manager
 *  Copyright (c) 2015-2021 Sergii Stoian
 *
 *  WINGs library (Window Maker)
 *  Copyright (c) 1998 scottc
 *  Copyright (c) 1999-2004 Dan Pascu
 *  Copyright (c) 1999-2000 Alfredo K. Kojima
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <ctype.h>
#include <string.h>

#include <wraster.h>

#include "wscreen.h"
#include "wview.h"
#include "wpixmap.h"
#include "wcolor.h"
#include "drawing.h"

void WMDrawRelief(WMScreen *scr, Drawable d, int x, int y, unsigned int width,
                  unsigned int height, WMReliefType relief)
{
  WMDrawReliefWithGC(scr, d, x, y, width, height, relief,
                     WMColorGC(scr->black), WMColorGC(scr->darkGray),
                     WMColorGC(scr->gray), WMColorGC(scr->white));
}

void WMDrawReliefWithGC(WMScreen *scr, Drawable d, int x, int y, unsigned int width,
                        unsigned int height, WMReliefType relief,
                        GC black, GC dark, GC light, GC white)
{
  Display *dpy = scr->display;
  GC bgc;
  GC wgc;
  GC lgc;
  GC dgc;

  switch (relief) {
  case WRSimple:
    XDrawRectangle(dpy, d, black, x, y, width - 1, height - 1);
    return;

  case WRRaised:
    bgc = black;
    dgc = dark;
    wgc = white;
    lgc = light;
    break;

  case WRSunken:
    wgc = dark;
    lgc = black;
    bgc = white;
    dgc = light;
    break;

  case WRPushed:
    lgc = wgc = black;
    dgc = bgc = white;
    break;

  case WRRidge:
    lgc = bgc = dark;
    dgc = wgc = white;
    break;

  case WRGroove:
    wgc = dgc = dark;
    lgc = bgc = white;
    break;

  default:
    return;
  }
  /* top left */
  XDrawLine(dpy, d, wgc, x, y, x + width - 1, y);
  if (width > 2 && relief != WRRaised && relief != WRPushed) {
    XDrawLine(dpy, d, lgc, x + 1, y + 1, x + width - 3, y + 1);
  }

  XDrawLine(dpy, d, wgc, x, y, x, y + height - 1);
  if (height > 2 && relief != WRRaised && relief != WRPushed) {
    XDrawLine(dpy, d, lgc, x + 1, y + 1, x + 1, y + height - 3);
  }

  /* bottom right */
  XDrawLine(dpy, d, bgc, x, y + height - 1, x + width - 1, y + height - 1);
  if (width > 2 && relief != WRPushed) {
    XDrawLine(dpy, d, dgc, x + 1, y + height - 2, x + width - 2, y + height - 2);
  }

  XDrawLine(dpy, d, bgc, x + width - 1, y, x + width - 1, y + height - 1);
  if (height > 2 && relief != WRPushed) {
    XDrawLine(dpy, d, dgc, x + width - 2, y + 1, x + width - 2, y + height - 2);
  }
}

static int _nextWordPosition(const char *text, int limit)
{
  int pos, len;

  len = strcspn(text, " \t\n\r");
  pos = len + strspn(text + len, " \t\n\r");
  if (pos > limit)
    pos = limit;

  return pos;
}

static int _fitText(const char *text, WMFont *font, int width, int wrap)
{
  int i, w, beforecrlf, word1, word2;

  /* text length before first cr/lf */
  beforecrlf = strcspn(text, "\n");

  if (!wrap || beforecrlf == 0)
    return beforecrlf;

  w = WMWidthOfString(font, text, beforecrlf);
  if (w <= width) {
    /* text up to first crlf fits */
    return beforecrlf;
  }

  word1 = 0;
  while (1) {
    word2 = word1 + _nextWordPosition(text + word1, beforecrlf - word1);
    if (word2 >= beforecrlf)
      break;
    w = WMWidthOfString(font, text, word2);
    if (w > width)
      break;
    word1 = word2;
  }

  for (i = word1; i < word2; i++) {
    w = WMWidthOfString(font, text, i);
    if (w > width) {
      break;
    }
  }

  /* keep words complete if possible */
  if (!isspace(text[i]) && word1 > 0) {
    i = word1;
  } else if (isspace(text[i]) && i < beforecrlf) {
    /* keep space on current row, so new row has next word in column 1 */
    i++;
  }

  return i;
}

int WMGetTextHeight(WMFont *font, const char *text, int width, int wrap)
{
  const char *ptr = text;
  int count;
  int length = strlen(text);
  int h;
  int fheight = WMFontHeight(font);

  h = 0;
  while (length > 0) {
    count = _fitText(ptr, font, width, wrap);

    h += fheight;

    if (isspace(ptr[count]))
      count++;

    ptr += count;
    length -= count;
  }
  return h;
}

void
WMPaintText(WMView *view, Drawable d, WMFont *font, int x, int y,
	    int width, WMAlignment alignment, WMColor *color, int wrap,
	    const char *text, int length)
{
  const char *ptr = text;
  int line_width;
  int line_x;
  int count;
  int fheight = WMFontHeight(font);

  while (length > 0) {
    count = _fitText(ptr, font, width, wrap);

    line_width = WMWidthOfString(font, ptr, count);
    if (alignment == WALeft)
      line_x = x;
    else if (alignment == WARight)
      line_x = x + width - line_width;
    else
      line_x = x + (width - line_width) / 2;

    WMDrawString(view->screen, d, color, font, line_x, y, ptr, count);

    if (wrap && ptr[count] != '\n')
      y += fheight;

    while (ptr[count] && ptr[count] == '\n') {
      y += fheight;
      count++;
    }

    ptr += count;
    length -= count;
  }
}

void
WMPaintTextAndImage(WMView *view, int wrap, WMColor *textColor, WMFont *font,
		    WMReliefType relief, const char *text,
		    WMAlignment alignment, WMPixmap *image,
		    WMImagePosition position, WMColor *backColor, int ofs)
{
  WMScreen *screen = view->screen;
  int ix, iy;
  int x, y, w, h;
  Drawable d = view->window;

#ifdef DOUBLE_BUFFER
  d = XCreatePixmap(screen->display, view->window, view->size.width, view->size.height, screen->depth);
#endif

  /* background */
  if (backColor) {
    XFillRectangle(screen->display, d, WMColorGC(backColor),
                   0, 0, view->size.width, view->size.height);
  } else {
    if (view->attribs.background_pixmap) {
#ifndef DOUBLE_BUFFER
      XClearWindow(screen->display, d);
#else
      XCopyArea(screen->display, view->attribs.background_pixmap, d, screen->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
#endif
    } else {
#ifndef DOUBLE_BUFFER
      XClearWindow(screen->display, d);
#else
      XSetForeground(screen->display, screen->copyGC, view->attribs.background_pixel);
      XFillRectangle(screen->display, d, screen->copyGC, 0, 0, view->size.width, view->size.height);
#endif
    }
  }

  if (relief == WRFlat) {
    x = 0;
    y = 0;
    w = view->size.width;
    h = view->size.height;
  } else {
    x = 1;
    y = 1;
    w = view->size.width - 3;
    h = view->size.height - 3;
  }

  /* calc. image alignment */
  if (position != WIPNoImage && image != NULL) {
    switch (position) {
    case WIPOverlaps:
    case WIPImageOnly:
      ix = (view->size.width - image->width) / 2;
      iy = (view->size.height - image->height) / 2;
      /*
        x = 2;
        y = 0;
      */
      break;

    case WIPLeft:
      ix = x;
      iy = y + (h - image->height) / 2;
      x = x + image->width + 5;
      y = 0;
      w -= image->width + 5;
      break;

    case WIPRight:
      ix = view->size.width - image->width - x;
      iy = y + (h - image->height) / 2;
      w -= image->width + 5;
      break;

    case WIPBelow:
      ix = (view->size.width - image->width) / 2;
      iy = h - image->height;
      y = 0;
      h -= image->height;
      break;

    default:
    case WIPAbove:
      ix = (view->size.width - image->width) / 2;
      iy = y;
      y = image->height;
      h -= image->height;
      break;
    }

    ix += ofs;
    iy += ofs;

    XSetClipOrigin(screen->display, screen->clipGC, ix, iy);
    XSetClipMask(screen->display, screen->clipGC, image->mask);

    if (image->depth == 1)
      XCopyPlane(screen->display, image->pixmap, d, screen->clipGC,
                 0, 0, image->width, image->height, ix, iy, 1);
    else
      XCopyArea(screen->display, image->pixmap, d, screen->clipGC,
                0, 0, image->width, image->height, ix, iy);
  }

  /* draw text */
  if (position != WIPImageOnly && text != NULL) {
    int textHeight;

    textHeight = WMGetTextHeight(font, text, w - 8, wrap);
    WMPaintText(view, d, font, x + ofs + 4, y + ofs + (h - textHeight) / 2, w - 8,
                alignment, textColor, wrap, text, strlen(text));
  }

  /* draw relief */
  WMDrawRelief(screen, d, 0, 0, view->size.width, view->size.height, relief);

#ifdef DOUBLE_BUFFER
  XCopyArea(screen->display, d, view->window, screen->copyGC, 0, 0,
            view->size.width, view->size.height, 0, 0);
  XFreePixmap(screen->display, d);
#endif
}

WMPoint WMMakePoint(int x, int y)
{
  WMPoint point;

  point.x = x;
  point.y = y;

  return point;
}

WMSize WMMakeSize(unsigned int width, unsigned int height)
{
  WMSize size;

  size.width = width;
  size.height = height;

  return size;
}

WMRect WMMakeRect(int x, int y, unsigned int width, unsigned int height)
{
  WMRect rect;

  rect.pos.x = x;
  rect.pos.y = y;
  rect.size.width = width;
  rect.size.height = height;

  return rect;
}
